package ddz.core.ai;

import com.kaka.util.IntArray;
import com.kaka.util.IntSet;
import com.kaka.util.StringUtils;
import ddz.core.CardType;
import ddz.core.Cards;
import ddz.core.Pile;

import java.util.*;

/**
 * 癞子玩法相关算法
 */
public class AILaizi {

//    private boolean isDan(HandCards mycards) {
//        return mycards.getCardCount() == 1;
//    }
//
//    private boolean isDui(HandCards mycards, int[] laizi) {
//        Pile wang = mycards.getPileByIndex(HandCards.getIndex(0));
//        if (wang != null && wang.getCount() > 0) {
//            return false;
//        }
//        if (mycards.getCardCount() == 2) {
//            if (mycards.getPileCount() == 1) {
//                return true;
//            }
//            if (mycards.getPileCount() == 2) {
//                if (laizi != null && laizi.length > 0 && mycards.hasCard(laizi[0], 1)) {
//                    return true;
//                }
//            }
//        }
//        return false;
//    }
//
//    private boolean isSan(HandCards mycards, int[] laizi) {
//        Pile wang = mycards.getPileByIndex(HandCards.getIndex(0));
//        if (wang != null && wang.getCount() > 0) {
//            return false;
//        }
//        if (mycards.getCardCount() == 3) {
//            if (mycards.getPileCount() == 1) {
//                return true;
//            }
//            if (mycards.getPileCount() == 2) {
//                if (laizi != null && laizi.length > 0 && mycards.hasCard(laizi[0], 1)) {
//                    return true;
//                }
//            }
//        }
//        return false;
//    }
//
//    private boolean isZha(HandCards mycards, int[] laizi) {
//        Pile wang = mycards.getPileByIndex(HandCards.getIndex(0));
//        if (wang != null && wang.getCount() == 2 && mycards.getCardCount() == wang.getCount()) {
//            return true;
//        }
//        if (wang != null && wang.getCount() > 0) {
//            return false;
//        }
//        if (mycards.getCardCount() == 4) {
//            if (mycards.getPileCount() == 1) {
//                return true;
//            }
//            if (mycards.getPileCount() == 2) {
//                if (laizi != null && laizi.length > 0 && mycards.hasCard(laizi[0], 1)) {
//                    return true;
//                }
//            }
//        }
//        return false;
//    }
//
//    private boolean isSandaidan(HandCards mycards, int[] laizi) {
//        if (laizi == null || laizi.length == 0) {
//            return AI0.isSandaidan(mycards);
//        }
//        if (mycards.getCardCount() != 4) {
//            return false;
//        }
//        if (mycards.getPileCount() == 2) {
//            return true;
//        }
//        if (mycards.getPileCount() == 3) {
//            if (laizi.length <= 2) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//    private boolean isSandaidui(HandCards mycards, int[] laizi) {
//        if (laizi == null || laizi.length == 0) {
//            return AI0.isSandaidui(mycards);
//        }
//        if (mycards.getCardCount() != 5) {
//            return false;
//        }
//        if (mycards.getPileCount() == 2) {
//            return true;
//        }
//        if (mycards.getPileCount() == 3) {
//            int laiziPoint = HandCards.getPoint(laizi[0]);
//            int duiCount = 0, danCount = 0;
//            for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
//                Pile cluster = mycards.getPileByIndex(i);
//                if (cluster != null && cluster.getPoint() != laiziPoint) {
//                    if (cluster.getCount() == 2) {
//                        duiCount++;
//                    } else if (cluster.getCount() == 1) {
//                        danCount++;
//                    }
//                }
//            }
//            if (laizi.length == 1) {
//                if (duiCount == 2 && danCount == 0) return true;
//            }
//            if (laizi.length == 2) {
//                if (duiCount == 1 && danCount == 1) return true;
//            }
//            if (laizi.length == 3) {
//                if (duiCount == 0 && danCount == 2) return true;
//            }
//        }
//        return false;
//    }
//
//    /**
//     * 补齐牌簇的张数到指定目标数量
//     *
//     * @param mycards     手牌
//     * @param point       需补齐的牌点
//     * @param targetCount 需补齐的目标数量
//     * @param laiziArray  癞子牌暂存器
//     * @param addedCards  已补的牌
//     * @return 补齐后的牌簇对象
//     */
//    private static Pile compensate(HandCards mycards, int point, int targetCount, IntArray laiziArray, IntSet addedCards) {
//        Pile pile = mycards.getPileByIndex(HandCards.getIndex(point));
//        if (laiziArray.size() <= 0) return pile;
//        if (pile == null) {
//            if (laiziArray.size() < targetCount) return pile;
//            for (int i = 0; i < targetCount; i++) {
//                int laiziCard = laiziArray.pop();
//                int suit = HandCards.getSuit(laiziCard);
//                int newCard = suit * 100 + point;
//                mycards.addCard(newCard);
//                addedCards.add(newCard);
//            }
//        } else {
//            if (pile.getCount() == targetCount) return pile;
//            int offsetCount = targetCount - pile.getCount();
//            if (laiziArray.size() < offsetCount) return pile;
//            int n = 0;
//            for (int suit = 1; suit <= 4 && n < offsetCount; suit++) {
//                int newCard = suit * 100 + point;
//                if (mycards.addCard(newCard)) {
//                    laiziArray.pop();
//                    n++;
//                    addedCards.add(newCard);
//                }
//            }
//        }
//        return mycards.getPileByIndex(HandCards.getIndex(point));
//    }
//
//    /**
//     * 手牌有补齐行为后对手牌进行还原
//     *
//     * @param mycards    手牌
//     * @param addedCards 补齐时创建添加的牌
//     * @param laizi      癞子牌
//     */
//    private static void restore(HandCards mycards, IntSet addedCards, int[] laizi) {
//        IntSet.IntSetIterator iterator = addedCards.iterator();
//        while (iterator.hasNext) {
//            mycards.removeCard(iterator.next());
//        }
//        mycards.addCard(laizi);
//    }
//
//    /**
//     * 是否为连顺，可判断单顺、对顺、三顺
//     *
//     * @param mycards  手牌
//     * @param shunNum  顺子最小个数，单顺为5，双顺为3，三顺、四顺为2
//     * @param shunType 顺子中每一簇牌的张数，单顺为1，对顺为2，三顺为3
//     * @param laizi    癞子牌
//     * @return true为顺牌
//     */
//    private static boolean isShun(HandCards mycards, int shunNum, int shunType, int[] laizi) {
//        Pile _2 = mycards.getPileByIndex(HandCards.getIndex(2));
//        if (_2 != null && _2.getCount() > 0) {
//            return false;
//        }
//        Pile _wang = mycards.getPileByIndex(HandCards.getIndex(0));
//        if (_wang != null && _wang.getCount() > 0) {
//            return false;
//        }
//        int laiziPoint = HandCards.getPoint(laizi[0]);
//        mycards.removePile(HandCards.getIndex(laiziPoint)); //移除癞子牌
//        IntSet addedCards = new IntSet(laizi.length);
//        IntArray laiziArray = new IntArray(laizi);
//        //将癞子牌插入空位
//        Pile startPile = null;
//        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
//            Pile currPile = mycards.getPileByIndex(i);
//            if (currPile != null && currPile.getCount() > shunType) {
//                restore(mycards, addedCards, laizi);
//                return false;
//            }
//            if (startPile == null) {
//                if (currPile != null) {
//                    startPile = compensate(mycards, i, shunType, laiziArray, addedCards);
//                }
//                continue;
//            }
//            currPile = compensate(mycards, i, shunType, laiziArray, addedCards);
//            if (currPile == null || currPile.getCount() == 0) break;
//            if (!startPile.isSequenceOf(currPile)) {
//                restore(mycards, addedCards, laizi);
//                return false;
//            }
//            startPile = currPile;
//        }
//        if (AI0.isShun(mycards, mycards.getPileCount(), shunType)) {
//            int remainingLaizi = laiziArray.size();
//            int shunCount = mycards.getPileCount() + remainingLaizi / shunType;
//            if (shunCount <= 13 && shunCount >= shunNum) {
//                restore(mycards, addedCards, laizi);
//                return true;
//            }
//        }
//        restore(mycards, addedCards, laizi);
//        return false;
//    }
//
//    public static boolean isShunzi(HandCards mycards, int[] laizi) {
//        if (laizi == null || laizi.length == 0) {
//            return AI0.isShunzi(mycards);
//        }
//        if (mycards.getCardCount() < 5) {
//            return false;
//        }
//        return isShun(mycards, 5, 1, laizi);
//    }
//
//    public static boolean isLiandui(HandCards mycards, int[] laizi) {
//        if (laizi == null || laizi.length == 0) {
//            return AI0.isLiandui(mycards);
//        }
//        if (mycards.getCardCount() < 6 || mycards.getCardCount() % 2 != 0) {
//            return false;
//        }
//        return isShun(mycards, 3, 2, laizi);
//    }
//
//    public static boolean isFeiji(HandCards mycards, int[] laizi) {
//        if (laizi == null || laizi.length == 0) {
//            return AI0.isFeiji(mycards);
//        }
//        if (mycards.getCardCount() < 6 || mycards.getCardCount() % 3 != 0) {
//            return false;
//        }
//        return isShun(mycards, 2, 3, laizi);
//    }
//
//    public static boolean isLianzha(HandCards mycards, int[] laizi) {
//        if (laizi == null || laizi.length == 0) {
//            return AI0.isLianzha(mycards);
//        }
//        if (mycards.getCardCount() < 8 || mycards.getCardCount() % 4 != 0) {
//            return false;
//        }
//        return isShun(mycards, 2, 4, laizi);
//    }
//
//    public static boolean isSidai1(HandCards mycards, int[] laizi) {
//        if (mycards.getCardCount() != 5) return false;
//        int laiziPoint = HandCards.getPoint(laizi[0]);
//        Pile laiziPile = mycards.removePile(HandCards.getIndex(laiziPoint));
//        if (mycards.getPileCount() == 1) {
//
//        }
//        return false;
//    }

    ///////////////////////////////////////////////////////

    public static List<int[]> dan(Cards mycards, int minCard) {
        //普通单牌算法
        return AI.dan(mycards, minCard);
    }

    public static List<int[]> dui(Cards mycards, int minCard, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            List<int[]> list = AI.dui(mycards, minCard);
            List<int[]> newList = new ArrayList<>(list.size());
            for (int[] duiCards : list) {
                newList.add(duiCards);
            }
            return newList;
        }
        return dui_san_zha(mycards, minCard, 2, laizi);
    }

    public static List<int[]> san(Cards mycards, int minCard, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.san(mycards, minCard);
        }
        return dui_san_zha(mycards, minCard, 3, laizi);
    }

    public static List<int[]> zha(Cards mycards, int minCard, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.zha(mycards, minCard);
        }
        List<int[]> zhaList = dui_san_zha(mycards, minCard, 4, laizi);
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() == 2) {
            zhaList.add(wang.getCards());
        }
        return zhaList;
    }

    public static List<int[]> dui_san_zha(Cards mycards, int minCard, int type, int[] laizi) {
        Pile laiziPile = null;
        IntSet addedCards = null; //用癞子补齐牌的时候新创建的牌，用于提取补齐后的牌后再次还原牌簇中的牌
        if (laizi != null && laizi.length > 0) {
            laiziPile = mycards.removePile(Cards.getIndex(Cards.getPoint(laizi[0]))); //移除癞子牌
            addedCards = new IntSet(laizi.length);
        }
        List<int[]> list = new ArrayList<>();
        //确定最大循环索引
        int max = mycards.getMaxValidIndex();
        int _2_idx = Cards.getIndex(2);
        if (max > _2_idx) max = _2_idx;
        for (int i = mycards.getMinValidIndex(); i <= max; i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.compare(minCard) > 0) {
                if (pile.getCount() >= 1) {
                    int[] cards = compensate(pile, type, laizi, addedCards);
                    if (cards != null) {
                        list.add(cards);
                    }
                }
            }
        }
        //还原移除的癞子牌
        if (laiziPile != null) mycards.insertPile(laiziPile);
        if (laizi != null && laizi.length >= type && laiziPile.compare(minCard) > 0) {
            //癞子牌本身满足条件
            int[] cards = new int[type];
            System.arraycopy(laizi, 0, cards, 0, cards.length);
            list.add(cards);
        }
        return list;
    }

    /**
     * 从手牌中移除指定的牌
     *
     * @param mycards
     * @param cards
     * @param tempRemovedCards
     */
    private static void remove(Cards mycards, int[] cards, IntSet tempRemovedCards) {
        for (int c : cards) {
            int card = c;
            if (card > 1000) { //六位数的牌为被癞子替代的牌，所以得分离出真实的癞子牌后从手牌中移除
                card = Cards.spliteBackCard(card);
            } else {
                int pileIndex = Cards.getIndex(card);
                Pile pile = mycards.getPileByIndex(pileIndex);
                if (pile != null && pile.getCount() > 0) {
                    int[] _cards = pile.getCards();
                    for (int cc : _cards) {
                        mycards.removeCard(cc);
                        tempRemovedCards.add(cc);
                    }
                }
            }
            mycards.removeCard(card);
            tempRemovedCards.add(card);
        }
    }

    private static void restore(Cards mycards, IntSet tempRemovedCards) {
        IntSet.IntSetIterator iterator = tempRemovedCards.iterator();
        while (iterator.hasNext) {
            int card = iterator.next();
            mycards.addCard(card);
        }
    }

    /**
     * 三带一或者四带一中被带的一张牌集合
     *
     * @param mycards    手牌
     * @param laiziIndex 癞子牌
     * @return 被附带的单张牌的集合
     */
    private static List<CardWrap> danOfDai(Cards mycards, int laiziIndex) {
        List<CardWrap> danCards = new ArrayList<>(mycards.getPileCount());
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.getCount() > 0) {
                int[] pileCards = pile.getCards();
                //单牌基础权重为100，癞子牌为700，鬼牌为300，对为400，三为500，四为800，王炸为1000
                for (int card : pileCards) {
                    CardWrap wrap = new CardWrap(new int[]{card});
                    if (pile.getCount() == 1) {
                        if (pile.getPoint() == 0) {
                            wrap.weight = 300;
                        } else {
                            wrap.weight = 100;
                        }
                    } else if (pile.getCount() == 2) {
                        if (pile.getPoint() == 0) {
                            wrap.weight = 1000;
                        } else {
                            wrap.weight = 400;
                        }
                    } else if (pile.getCount() == 3) {
                        wrap.weight = 500;
                    } else if (pile.getCount() == 4) {
                        wrap.weight = 800;
                    }
                    if (i == laiziIndex && pile.getCount() >= 1) {
                        wrap.weight = 700;
                    }
                    //单牌的基础权重还得加上牌点，根据牌点也是从小到大
                    int p = pile.getPoint();
                    if (p == 2) p = 20;
                    wrap.weight += p;
                    danCards.add(wrap);
                }
            }
        }
        //根据单张的权重排序，
        CardWrap.sortCardWrapList(danCards);
        return danCards;
    }

    /**
     * 三带对或者四带对附带对子集合
     *
     * @param mycards     手牌
     * @param laizi       癞子牌
     * @param zhaToTwoDui 四张炸弹是否拆成两对
     * @return
     */
    private static List<CardWrap> duiOfDai(Cards mycards, int[] laizi, boolean zhaToTwoDui) {
        int max = mycards.getMaxValidIndex();
        int _2_idx = Cards.getIndex(2);
        if (max > _2_idx) max = _2_idx;

        int laiziIndex = -1;
        if (laizi != null && laizi.length > 0) {
            laiziIndex = Cards.getIndex(laizi[0]);
        }

        IntSet addedCards = new IntSet(1);
        List<CardWrap> duiList = new ArrayList<>(mycards.getPileCount());
        for (int i = mycards.getMinValidIndex(); i <= max; i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.getCount() >= 1) {
                //基础权重：纯癞子牌为700，本身对为100，三为500，四为800，有癞子填充的对为300
                if (pile.getCount() == 1) {
                    if (i != laiziIndex && laizi != null && laizi.length >= 1) {
                        //int[] pileCards = pile.getCards();
                        int[] pileCards = compensate(pile, 2, laizi, addedCards);
                        CardWrap wrap = new CardWrap(pileCards);
                        wrap.weight = 300;
                        wrap.hasLaizi = true;
                        duiList.add(wrap);
                    }
                } else if (pile.getCount() == 4 && zhaToTwoDui) {
                    int[] pileCards = pile.getCards();
                    CardWrap wrap1 = new CardWrap(new int[]{pileCards[0], pileCards[1]});
                    wrap1.weight = 800;
                    duiList.add(wrap1);
                    CardWrap wrap2 = new CardWrap(new int[]{pileCards[2], pileCards[3]});
                    wrap2.weight = 800;
                    duiList.add(wrap2);
                } else {
                    int[] pileCards = pile.getCards();
                    CardWrap wrap = new CardWrap(pileCards);
                    if (pile.getCount() == 2 && pile.getPoint() != 0) {
                        wrap.weight = 100;
                    } else if (pile.getCount() == 3) {
                        wrap.weight = 500;
                    } else if (pile.getCount() == 4 && !zhaToTwoDui) {
                        wrap.weight = 800;
                    }
                    if (i == laiziIndex && pile.getCount() >= 2) {
                        wrap.weight = 700;
                    }
                    duiList.add(wrap);
                }
            }
        }
        //根据单张的权重排序，
        CardWrap.sortCardWrapList(duiList);
        return duiList;
    }

    /**
     * 三带一或者四带两张
     *
     * @param mycards 手牌
     * @param type    3为三带一，4为四带两张
     * @param minCard 三张或四张必须大于此牌
     * @param laizi   癞子牌
     * @return
     */
    private static List<int[]> san_si_1(Cards mycards, int type, int minCard, int[] laizi) {
        int minAmount = type + type / 2;
        if (mycards.getCardCount() < minAmount) return new ArrayList<>(0);
        List<int[]> sanOrZha = (type == 3 ? san(mycards, minCard, laizi) : zha(mycards, minCard, laizi));
        if (sanOrZha.isEmpty()) return new ArrayList<>(0);

        int daiAmount = type / 2; //附带单牌数量
        int laiziIndex = -1;
        if (laizi != null && laizi.length > 0) {
            laiziIndex = Cards.getIndex(laizi[0]);
        }

        IntSet tempRemovedCards = new IntSet(minAmount);
        List<int[]> groups = new ArrayList<>();
        for (int[] cards : sanOrZha) {
            if(cards.length == 2) continue; //移除王炸
            //从手牌中移除主牌
            remove(mycards, cards, tempRemovedCards);
            //移除三张或四张主牌后从余下的牌中寻找单张，无单张拆2，无2拆3，无3拆炸
            List<CardWrap> danCards = danOfDai(mycards, laiziIndex);
            int[] group = new int[minAmount];
            System.arraycopy(cards, 0, group, 0, cards.length);
            int arrIndex = cards.length;
            for (int i = 0; i < daiAmount; i++, arrIndex++) {
                CardWrap wrap = danCards.get(i);
                int card = wrap.cards[0];
                group[arrIndex] = card;
            }
            groups.add(group); //新增一组三带一张或者四带两张
            //还原手牌
            restore(mycards, tempRemovedCards);
        }
        return groups;
    }

    /**
     * 三带对或者四带两对
     *
     * @param mycards 手牌
     * @param type    3为三带对，4为四带两对
     * @param minCard 三张或四张必须大于此牌
     * @param laizi   癞子牌
     * @return
     */
    private static List<int[]> san_si_2(Cards mycards, int type, int minCard, int[] laizi) {
        int minAmount = type + ((type / 2) * 2);
        if (mycards.getCardCount() < 5) return new ArrayList<>(0);
        List<int[]> sanOrZha = (type == 3 ? san(mycards, minCard, laizi) : zha(mycards, minCard, laizi));
        if (sanOrZha.isEmpty()) return new ArrayList<>(0);

        int daiAmount = type / 2; //附带的对牌数量
        int laiziIndex = -1; //癞子牌在手牌中的索引
        if (laizi != null && laizi.length > 0) laiziIndex = Cards.getIndex(laizi[0]);

        IntSet tempRemovedCards = new IntSet(minAmount); //临时保存从手牌中移除的牌，后面还得还原手牌
        List<int[]> groups = new ArrayList<>();

        for (int[] cards : sanOrZha) {
            if(cards.length == 2) continue; //移除王炸
            //从手牌中移除三张或四张主牌
            remove(mycards, cards, tempRemovedCards);
            Pile laiziPile = mycards.getPileByIndex(laiziIndex);
            int[] newLaizi = null;
            if (laiziPile != null) {
                newLaizi = laiziPile.getCards();
            }
            //移除三张或四张主牌后从余下的牌中寻找对牌
            List<CardWrap> duiList = duiOfDai(mycards, newLaizi, false);
            if (duiList.size() >= daiAmount) {
                int[] group = new int[minAmount];
                System.arraycopy(cards, 0, group, 0, cards.length);
                IntArray laiziArr = null;
                if (newLaizi != null && newLaizi.length > 0) laiziArr = new IntArray(newLaizi);
                int[] daiCards = new int[daiAmount * 2]; //暂存附带的对子牌
                int index = 0; //附带牌暂存数组的写入索引
                for (int i = 0; i < daiAmount; i++) {
                    CardWrap duiWrap = duiList.get(i);
                    int[] duiCards = null;
                    if (duiWrap.hasLaizi) {
                        if (laiziArr != null && laiziArr.size() > 0) {
                            //因为带多对时，对子中的癞子不能使用重复的，故而需要把已使用的癞子牌替换为未使用的
                            int[] duiWrapCards = duiWrap.cards;
                            for (int j = 0; j < duiWrapCards.length; j++) {
                                int duiCard = duiWrapCards[j];
                                if (duiCard > 1000) {
                                    //替换掉癞子牌
                                    duiCard = Cards.spliteForeCard(duiCard);
                                    int laiziCard = laiziArr.pop(); //未曾使用的癞子牌，将替换掉对子中的癞子牌，保证连续的两对中没有重复的癞子
                                    int newCard = Cards.assembleSixCard(duiCard, laiziCard);
                                    duiWrapCards[j] = newCard;
                                    break;
                                }
                            }
                            duiCards = duiWrap.cards;
                        }
                    } else {
                        duiCards = duiWrap.cards;
                    }
                    if (duiCards != null) {
                        //每找到一对牌就将其复制到附带牌暂存器中
                        System.arraycopy(duiCards, 0, daiCards, index, 2);
                        index += 2;
                    }
                }
                if (index == daiCards.length) {
                    int arrIndex = cards.length;
                    //达到附带总量后即将此附带牌复制到三张或四张牌后面构成一组牌
                    System.arraycopy(daiCards, 0, group, arrIndex, daiCards.length);
                }
                groups.add(group); //新增一组三带一对或者四带两对
            }
            //还原手牌
            restore(mycards, tempRemovedCards);
        }
        return groups;
    }

    public static List<int[]> sandai1(Cards mycards, int minCard, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.sandai1(mycards, minCard);
        }
        return san_si_1(mycards, 3, minCard, laizi);
    }

    public static List<int[]> sandai2(Cards mycards, int minCard, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.sandai2(mycards, minCard);
        }
        return san_si_2(mycards, 3, minCard, laizi);
    }

    public static List<int[]> sidai1(Cards mycards, int minCard, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.sidai1(mycards, minCard);
        }
        return san_si_1(mycards, 4, minCard, laizi);
    }

    public static List<int[]> sidai2(Cards mycards, int minCard, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.sidai2(mycards, minCard);
        }
        return san_si_2(mycards, 4, minCard, laizi);
    }

    /**
     * 对牌簇补齐到目标数量的牌
     *
     * @param pile        牌簇对象，不能为空
     * @param targetCount 目标数量，如：一对为2，三张为3，四张炸为4
     * @param laizi       癞子牌
     * @param addedCards  补的新牌，用于还原牌簇
     * @return 补齐后的牌
     */
    private static int[] compensate(Pile pile, int targetCount, int[] laizi, IntSet addedCards) {
        if (pile.getCount() >= targetCount) {
            int[] cards = pile.getCards();
            int[] copy = new int[targetCount];
            System.arraycopy(cards, 0, copy, 0, copy.length);
            return copy;
        }
        int offsetCount = targetCount - pile.getCount();
        if (laizi == null) return null;
        if (laizi.length < offsetCount) return null;
        int pilePoint = pile.getPoint();
        int laizPoint = Cards.getPoint(laizi[0]);
        int[] cards = pile.getCards();
        if (pilePoint == laizPoint) return cards;
        int n = 0;
        for (int suit = 4; suit >= 1 && n < offsetCount; suit--) {
            int newCard = suit * 100 + pile.getPoint();
            if (pile.addCard(newCard)) {
                addedCards.add(newCard);
                n++;
            }
        }
        if (n > 0) cards = pile.getCards();
        if (addedCards.size() > 0) {
            for (int i = 0, k = 0; i < cards.length; i++) {
                int card = cards[i];
                if (addedCards.contains(card)) {
                    pile.removeCard(card);
                    //组合为6位数的牌，前三位表示实际表示的牌，后三位表示癞子牌
                    cards[i] = Cards.assembleSixCard(card, laizi[k]);
                    k++;
                }
            }
            addedCards.clear();
        }
        return cards;
    }

    /**
     * 补齐牌簇的张数到指定目标数量
     *
     * @param mycards     手牌
     * @param point       需补齐的牌点
     * @param targetCount 需补齐的目标数量
     * @param laiziArray  癞子牌暂存器
     * @param addedCards  已补的牌
     * @return 补齐后的牌簇对象
     */
    private static int[] compensate(Cards mycards, int point, int targetCount, IntArray laiziArray, IntSet addedCards) {
        Pile pile = mycards.getPileByIndex(Cards.getIndex(point));
        if (pile == null || pile.getCount() == 0 || pile.getPoint() < 0) {
            if (laiziArray.size() < targetCount) return null;
            //全部用癞子补
            int[] cards = new int[targetCount];
            for (int i = 0; i < targetCount; i++) {
                int laiziCard = laiziArray.pop();
                int laiziPoint = Cards.getPoint(laiziCard);
                if (laiziPoint == point) {
                    cards[i] = laiziCard;
                } else {
                    int laiziSuit = Cards.getSuit(laiziCard);
                    int newCard = laiziSuit * 100 + point;
                    newCard = Cards.assembleSixCard(newCard, laiziCard);
                    cards[i] = newCard;
                }
            }
            return cards;
        }
        if (pile.getCount() >= targetCount) {
            int[] cards = pile.getCards();
            int[] copy = new int[targetCount];
            System.arraycopy(cards, 0, copy, 0, copy.length);
            return copy;
        }
        int offsetCount = targetCount - pile.getCount();
        if (laiziArray.size() < offsetCount) return null;
        int n = 0;
        for (int suit = 4; suit >= 1 && n < offsetCount; suit--) {
            int newCard = suit * 100 + pile.getPoint();
            if (mycards.addCard(newCard)) {
                addedCards.add(newCard);
                n++;
            }
        }
        pile = mycards.getPileByIndex(Cards.getIndex(point));
        int[] cards = pile.getCards();
        if (addedCards.size() > 0) {
            for (int i = 0; i < cards.length; i++) {
                int card = cards[i];
                if (addedCards.contains(card)) {
                    mycards.removeCard(card);
                    //组合为6位数的牌，前三位表示实际表示的牌，后三位表示癞子牌
                    cards[i] = Cards.assembleSixCard(card, laiziArray.pop());
                }
            }
            addedCards.clear();
        }
        return cards;
    }

    /**
     * 手牌有补齐行为后对手牌进行还原
     *
     * @param mycards    手牌
     * @param addedCards 补齐时创建添加的牌
     * @param laizi      癞子牌
     */
    private static void restore(Cards mycards, IntSet addedCards, int[] laizi) {
        IntSet.IntSetIterator iterator = addedCards.iterator();
        while (iterator.hasNext) {
            mycards.removeCard(iterator.next());
        }
        mycards.addCard(laizi);
    }

    /**
     * @param mycards          手牌
     * @param minCard          顺子的最小牌必须大于此牌
     * @param type             牌簇中最小牌张数，1为单顺，2为对顺，3为三顺
     * @param minShunAmount    最小顺个数
     * @param surplus_minCount 顺子数量是否可以超出指定的最小顺子数量
     * @param laizi            癞子
     * @return 顺子集合列表
     */
    public static List<int[]> shun(Cards mycards, int minCard, int type, int minShunAmount, boolean surplus_minCount, int[] laizi) {
        int laiziCount = 0;
        Pile laiziPile = null;
        if (laizi != null && laizi.length > 0) {
            laiziCount = laizi.length;
            int laiziPoint = Cards.getPoint(laizi[0]);
            laiziPile = mycards.removePile(Cards.getIndex(laiziPoint)); //移除癞子牌
        }
        int A_PileIndex = Cards.getIndex(14);

        List<int[]> groups = new ArrayList<>();
        Set<String> shunKeys = new HashSet<>();
        IntSet tempCompensate = new IntSet(); //临时存储补癞子时新补的牌，用于还原手牌

        for (int useLaiziCount = 0; useLaiziCount <= laiziCount; useLaiziCount++) { //使用癞子的个数
            int start = mycards.getMinValidIndex();
            int end = mycards.getMaxValidIndex();
            start -= laiziCount;
            end += laiziCount;
            if (start < 0) start = 0;
            if (end > A_PileIndex) end = A_PileIndex;
            int maxPileIndexOfShun = end - minShunAmount + 1; //顺子时的最大索引，如单顺时，最大五顺为10 J Q K A，而10的索引为A的索引减5再加1
            for (int i = start; i <= maxPileIndexOfShun; i++) {
                int maxLength = 0;
                for (int shunLen = minShunAmount; shunLen <= 12; shunLen++) { //循环顺的个数
                    int notEnoughCount = 0; //不足位计数
                    for (int k = i; k < i + shunLen && k <= end; k++) { //判断在顺的个数内有多少个空位或不足位
                        Pile pile = mycards.getPileByIndex(k);
                        if (pile == null || pile.getCount() <= 0 || pile.getPoint() < 0) {
                            notEnoughCount += type;
                        } else if (pile.getCount() < type) {
                            notEnoughCount += (type - pile.getCount());
                        }
                    }
                    if (notEnoughCount > useLaiziCount) {
                        //不足位个数大于使用的癞子个数时跳出当前循环，因为前面的顺个数都不满足，后面的更不用循环了
                        break;
                    }
                    maxLength = shunLen;
                }
                if (maxLength < minShunAmount) continue;
                IntArray laiziArray = null;
                if (laizi != null && laizi.length > 0) laiziArray = new IntArray(laizi);
                IntArray shun = new IntArray(maxLength);
                String shunKey = null;
                int minCardPoint = minCard <= 0 ? minCard : Cards.getPoint(minCard);
                int max = (maxLength >= end ? (end + 1) : (i + maxLength));
                for (int n = i; n < max; n++) { //循环获取指定长度的牌簇
                    int point = Cards.getPointByIndex(n);
                    if (point <= minCardPoint) { //顺子的最小牌是否大于指定的最小牌
                        int shunNum = max - n;
                        if (shunNum > minShunAmount) continue; //由于此时的n处的牌刚好是要求大于它的牌，所以不能大于或等于最小顺子个数，仅大于
                        else break; //顺子的最小牌小于或等于指定的最小牌且剩余顺子个数还小于最小顺子个数则只能跳出，省去下面的流程
                    }
                    if (shunKey == null) {
                        //过滤掉重复的顺子
                        int max_point = Cards.getPointByIndex(max);
                        int shunNum = max - n;
                        if (!surplus_minCount) {
                            shunNum = minShunAmount;
                            max_point = point + shunNum;
                        }
                        String startPointStr = StringUtils.repairZeroToFirst(point, 2); //顺子开始牌点
                        String lastPointStr = StringUtils.repairZeroToFirst(max_point, 2); //顺子结束牌点
                        String shunNumStr = StringUtils.repairZeroToFirst(shunNum, 2); //顺子个数
                        shunKey = startPointStr + "_" + shunNumStr + "_" + lastPointStr; //组合成顺子的唯一标识
                        if (shunKeys.contains(shunKey)) {
                            break;
                        }
                        shunKeys.add(shunKey);
                    }
                    int[] cards = compensate(mycards, point, type, laiziArray, tempCompensate);
                    if (cards != null) {
                        for (int idx = 0; idx < type; idx++) {
                            shun.add(cards[idx]);
                        }
                        int shunCount = shun.size() / type;
                        if (!surplus_minCount) {
                            if (shunCount == minShunAmount) break;
                        }
                    }
                }
                if (shun.size() >= minShunAmount) {
                    groups.add(shun.toArray());
                    //String str = String.format("maxIndex：%s (i: %d, 癞子数量：%d)  -%s-  %s", maxLength, i, useLaiziCount, shunKey, Arrays.toString(shun.toArray()));
                    //System.out.println(str);
                }
            }
        }
        if (laiziPile != null) {
            mycards.insertPile(laiziPile);
        }
        return groups;
    }

    private static List<int[]> danShun(Cards mycards, int minCard, int minShunAmount, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.danShun(mycards, minCard, minShunAmount);
        }
        return shun(mycards, minCard, 1, minShunAmount, false, laizi);
    }

    private static List<int[]> duiShun(Cards mycards, int minCard, int minShunAmount, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.duiShun(mycards, minCard, minShunAmount);
        }
        return shun(mycards, minCard, 2, minShunAmount, false, laizi);
    }

    private static List<int[]> sanShun(Cards mycards, int minCard, int minShunAmount, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.sanShun(mycards, minCard, minShunAmount);
        }
        return shun(mycards, minCard, 3, minShunAmount, false, laizi);
    }

    private static List<int[]> feiji(Cards mycards, int minCard, int minShunAmount, int daiType, int[] laizi) {
        List<int[]> sanShunList = sanShun(mycards, minCard, minShunAmount, laizi);
        List<int[]> groups = new ArrayList<>();
        if (sanShunList.isEmpty()) return groups;

        int laiziIndex = -1;
        if (laizi != null && laizi.length > 0) {
            laiziIndex = Cards.getIndex(laizi[0]);
        }

        IntSet tempRemovedCards = new IntSet(16);
        for (int[] sanShun : sanShunList) {
            remove(mycards, sanShun, tempRemovedCards);
            //移除三顺主牌后从余下的牌中寻找单张或对
            List<CardWrap> daiWraps;
            if (daiType == 1) {
                daiWraps = danOfDai(mycards, laiziIndex);
            } else {
                Pile laiziPile = mycards.getPileByIndex(laiziIndex);
                int[] newLaizi = null;
                if (laiziPile != null) {
                    newLaizi = laiziPile.getCards();
                }
                daiWraps = duiOfDai(mycards, newLaizi, true);
            }
            int shunCount = sanShun.length / 3;
            if (daiWraps.size() >= shunCount) {
                if (daiType == 1) {
                    int[] group = new int[sanShun.length + shunCount];
                    System.arraycopy(sanShun, 0, group, 0, sanShun.length);
                    int idx = sanShun.length;
                    for (int i = 0; i < shunCount; i++, idx++) {
                        CardWrap wrap = daiWraps.get(i);
                        int card = wrap.cards[0];
                        group[idx] = card;
                    }
                    groups.add(group); //新增一组三带一张或者四带两张
                } else {
                    int[] group = new int[sanShun.length + shunCount * 2];
                    System.arraycopy(sanShun, 0, group, 0, sanShun.length);
                    int idx = sanShun.length;
                    for (int i = 0; i < shunCount; i++) {
                        CardWrap wrap = daiWraps.get(i);
                        System.arraycopy(wrap.cards, 0, group, idx, 2);
                        idx += 2;
                    }
                    groups.add(group);
                }
            }
            //还原手牌
            restore(mycards, tempRemovedCards);
        }
        return groups;
    }

    public static List<int[]> feiji1(Cards mycards, int minCard, int minShunAmount, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.feiji1(mycards, minCard, minShunAmount);
        }
        return feiji(mycards, minCard, minShunAmount, 1, laizi);
    }

    public static List<int[]> feiji2(Cards mycards, int minCard, int minShunAmount, int[] laizi) {
        if (laizi == null || laizi.length == 0) {
            return AI.feiji2(mycards, minCard, minShunAmount);
        }
        return feiji(mycards, minCard, minShunAmount, 2, laizi);
    }

    public static List<int[]> beiDong(Cards mycards, Cards prevChuCards, CardType cardType, int laiziPoint) {
        Pile min = AI.getMinPile(prevChuCards, cardType);
        int minCard = min.getOneCard();
        int[] laizi = null;
        Pile laiziPile = mycards.getPileByIndex(Cards.getIndex(laiziPoint));
        if (laiziPile != null && laiziPile.getCount() > 0) {
            laizi = laiziPile.getCards();
        }
        List<int[]> groups = null;
        switch (cardType) {
            case dan:
                groups = AILaizi.dan(mycards, minCard);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case dui:
                groups = AILaizi.dui(mycards, minCard, laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case san:
                groups = AILaizi.san(mycards, minCard, laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case san_dan:
                groups = AILaizi.sandai1(mycards, minCard, laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case san_dui:
                groups = AILaizi.sandai2(mycards, minCard, laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case dan_shun:
                groups = AILaizi.danShun(mycards, minCard, prevChuCards.getPileCount(), laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case dui_shun:
                groups = AILaizi.duiShun(mycards, minCard, prevChuCards.getPileCount(), laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case san_shun:
                groups = AILaizi.sanShun(mycards, minCard, prevChuCards.getPileCount(), laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case feiji_dan:
                groups = AILaizi.feiji1(mycards, minCard, prevChuCards.getPileCount(), laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case feiji_dui:
                groups = AILaizi.feiji2(mycards, minCard, prevChuCards.getPileCount(), laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case si_dan:
                groups = AILaizi.sidai1(mycards, minCard, laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case si_dui:
                groups = AILaizi.sidai2(mycards, minCard, laizi);
                groups.addAll(AILaizi.zha(mycards, -1, laizi));
                break;
            case zha:
                groups = AILaizi.zha(mycards, minCard, laizi);
                break;
        }
        return groups;
    }

    public static List<int[]> zhuDong(Cards mycards, int laiziPoint) {
        int[] laizi = null;
        Pile laiziPile = mycards.getPileByIndex(Cards.getIndex(laiziPoint));
        if (laiziPile != null && laiziPile.getCount() > 0) {
            laizi = laiziPile.getCards();
        }
        List<int[]> groups = new ArrayList<>();
        groups.addAll(AILaizi.feiji1(mycards, -1, 2, laizi));
        groups.addAll(AILaizi.feiji2(mycards, -1, 2, laizi));
        groups.addAll(AILaizi.shun(mycards, -1, 3, 2, true, laizi));
        groups.addAll(AILaizi.sandai1(mycards, -1, laizi));
        groups.addAll(AILaizi.sandai2(mycards, -1, laizi));
        groups.addAll(AILaizi.shun(mycards, -1, 2, 3, true, laizi));
        groups.addAll(AILaizi.shun(mycards, -1, 1, 5, true, laizi));
        groups.addAll(AILaizi.san(mycards, -1, laizi));
        groups.addAll(AILaizi.dui(mycards, -1, laizi));
        groups.addAll(AILaizi.dan(mycards, -1));
        groups.addAll(AILaizi.sidai1(mycards, -1, laizi));
        groups.addAll(AILaizi.sidai2(mycards, -1, laizi));
        groups.addAll(AILaizi.zha(mycards, -1, laizi));
        return groups;
    }

//    public static void main(String[] args) {
//        int[] cards = new int[]{
//                209,109,410,310,205,305,307,408,306,406
//                //302, 102, 214, 413, 412, 212, 411, 311, 111, 409, 109, 407, 207, 107, 106, 405, 205, 105, 304, 103
//        };
//        Cards handCards = new Cards();
//        handCards.addCard(cards);
//        List<int[]> groups = feiji2(handCards, 0, 2, new int[] {205, 305});
//        System.out.println(Arrays.deepToString(groups.toArray()));
//    }

}
